//
//  CameraKitPreviewView.swift
//  CameraKit
//
//  Created by Will on 06/07/2020.
//  Copyright © 2020 Will. All rights reserved.
//

import UIKit
import AVFoundation

@objc public class CameraKitPreviewView: UIView {

    private var lastScale: CGFloat = 1.0

    @objc private(set) public var previewLayer: AVCaptureVideoPreviewLayer? {
        didSet {
            oldValue?.removeFromSuperlayer()

            if let previewLayer = previewLayer {
                layer.addSublayer(previewLayer)
            }
        }
    }

    @objc public var session: CameraKitSession? {
        didSet {
            oldValue?.stop()

            if let session = session {
                previewLayer = AVCaptureVideoPreviewLayer(session: session.session!)

                previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
                previewLayer?.connection?.videoOrientation = .portrait // 方向
                previewLayer?.connection?.isEnabled = true
                switch UIApplication.shared.statusBarOrientation {
                case .portrait:
                    previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
                case .portraitUpsideDown:
                    previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
                case .landscapeRight:
                    previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeRight
                case .landscapeLeft:
                    previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
                default: break
                }
                session.previewLayer = previewLayer
                session.overlayView = self
                session.start()
            }
        }
    }

    @objc private(set) public var gridView: XHBGridView? {
        didSet {
            oldValue?.removeFromSuperview()

            if let gridView = gridView {
                addSubview(gridView)
            }
        }
    }

    @objc public var showGrid: Bool = false {
        didSet {
            if showGrid == oldValue {
                return
            }

            if showGrid {
                gridView = XHBGridView(frame: bounds)
            } else {
                gridView = nil
            }
        }
    }

    @objc public var autorotate: Bool = false {
        didSet {
            if !autorotate {
                previewLayer?.connection?.videoOrientation = .portrait
            }
        }
    }

    @objc public override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    @objc public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupView()
    }

    private func setupView() {
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
        addGestureRecognizer(tapGestureRecognizer)

        let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(recognizer:)))
        addGestureRecognizer(pinchGestureRecognizer)

    }

    @objc private func handleTap(recognizer: UITapGestureRecognizer) {
        let location = recognizer.location(in: self)
        if let point = previewLayer?.captureDevicePointConverted(fromLayerPoint: location) {
            session?.focus(at: point)
            _showFocusRectangleAtPoint(location)
        }
    }
    fileprivate var lastFocusRectangle: CAShapeLayer?
    fileprivate var lastFocusPoint: CGPoint?

    fileprivate func _showFocusRectangleAtPoint(_ focusPoint: CGPoint, inLayer layer: CALayer? = nil, withBrightness brightness: Float? = nil) {
        if let lastFocusRectangle = lastFocusRectangle {
            lastFocusRectangle.removeFromSuperlayer()
            self.lastFocusRectangle = nil
        }

        let size = CGSize(width: 55, height: 55)
        let rect = CGRect(origin: CGPoint(x: focusPoint.x - size.width / 2.0, y: focusPoint.y - size.height / 2.0), size: size)

        let endPath = UIBezierPath(rect: rect)
        endPath.move(to: CGPoint(x: rect.minX + size.width / 2.0, y: rect.minY))
        endPath.addLine(to: CGPoint(x: rect.minX + size.width / 2.0, y: rect.minY + 5.0))
        endPath.move(to: CGPoint(x: rect.maxX, y: rect.minY + size.height / 2.0))
        endPath.addLine(to: CGPoint(x: rect.maxX - 5.0, y: rect.minY + size.height / 2.0))
        endPath.move(to: CGPoint(x: rect.minX + size.width / 2.0, y: rect.maxY))
        endPath.addLine(to: CGPoint(x: rect.minX + size.width / 2.0, y: rect.maxY - 5.0))
        endPath.move(to: CGPoint(x: rect.minX, y: rect.minY + size.height / 2.0))
        endPath.addLine(to: CGPoint(x: rect.minX + 5.0, y: rect.minY + size.height / 2.0))
        if brightness != nil {
            endPath.move(to: CGPoint(x: rect.minX + size.width + size.width / 4, y: rect.minY))
            endPath.addLine(to: CGPoint(x: rect.minX + size.width + size.width / 4, y: rect.minY + size.height))

            endPath.move(to: CGPoint(x: rect.minX + size.width + size.width / 4 - size.width / 16, y: rect.minY + size.height - CGFloat(brightness!) * size.height))
            endPath.addLine(to: CGPoint(x: rect.minX + size.width + size.width / 4 + size.width / 16, y: rect.minY + size.height - CGFloat(brightness!) * size.height))
        }

        let startPath = UIBezierPath(cgPath: endPath.cgPath)
        let baseTransform = CGAffineTransform(translationX: -focusPoint.x, y: -focusPoint.y)
        let scaleAroundCenterTransform = baseTransform.concatenating(CGAffineTransform(scaleX: 2.0, y: 2.0).concatenating(CGAffineTransform(translationX: focusPoint.x, y: focusPoint.y)))
        startPath.apply(scaleAroundCenterTransform)

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = endPath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor(red: 249/255.0, green: 216/255.0, blue: 86/255.0, alpha: 1).cgColor
        shapeLayer.lineWidth = 1.0

        previewLayer?.addSublayer(shapeLayer)
        lastFocusRectangle = shapeLayer
        lastFocusPoint = focusPoint

        CATransaction.begin()

        CATransaction.setAnimationDuration(0.2)
        CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut))

        CATransaction.setCompletionBlock {
            if shapeLayer.superlayer != nil {
                shapeLayer.removeFromSuperlayer()
                self.lastFocusRectangle = nil
            }
        }
        if brightness == nil {
            let appearPathAnimation = CABasicAnimation(keyPath: "path")
            appearPathAnimation.fromValue = startPath.cgPath
            appearPathAnimation.toValue = endPath.cgPath
            shapeLayer.add(appearPathAnimation, forKey: "path")

            let appearOpacityAnimation = CABasicAnimation(keyPath: "opacity")
            appearOpacityAnimation.fromValue = 0.0
            appearOpacityAnimation.toValue = 1.0
            shapeLayer.add(appearOpacityAnimation, forKey: "opacity")
        }

        let disappearOpacityAnimation = CABasicAnimation(keyPath: "opacity")
        disappearOpacityAnimation.fromValue = 1.0
        disappearOpacityAnimation.toValue = 0.0
        disappearOpacityAnimation.beginTime = CACurrentMediaTime() + 0.8
        disappearOpacityAnimation.fillMode = CAMediaTimingFillMode.forwards
        disappearOpacityAnimation.isRemovedOnCompletion = false
        shapeLayer.add(disappearOpacityAnimation, forKey: "opacity")

        CATransaction.commit()
    }

    @objc private func handlePinch(recognizer: UIPinchGestureRecognizer) {
        if recognizer.state == .began {
            recognizer.scale = lastScale
        }

        let zoom = max(1.0, min(10.0, recognizer.scale))
        session?.zoom = Double(zoom)

        if recognizer.state == .ended {
            lastScale = zoom
        }
    }

    public override func layoutSubviews() {
        super.layoutSubviews()
        previewLayer?.frame = bounds
        gridView?.frame = bounds

        if autorotate {
            previewLayer?.connection?.videoOrientation = UIDevice.current.orientation.videoOrientation
        }
    }
}
